package practise.alogrithm.hundredInterviewQuestions;

import com.sun.javafx.scene.control.skin.VirtualFlow;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by richard on 6/3/14.
 */
public class BlockQueueImpl<T> {
    List<T> container ;
    Lock getLock = new ReentrantLock();
    Lock putLock = new ReentrantLock();
    Condition getCondition = getLock.newCondition();
    Condition putCondition = putLock.newCondition();
    int size=0;
    boolean isLimit=false;
    public BlockQueueImpl(int size,boolean isLimit){
        container = new ArrayList<>(size);
        this.size = size;
        this.isLimit = isLimit;
    }
    public BlockQueueImpl(){
        container= new LinkedList<>();
    }

    public void offer(T obj){

        try {
            putLock.lockInterruptibly();
            if(isLimit&&size==container.size()){
                waitWhenFull();
            }
            if(container.size()+1<size){
                notifyNotFull();
            }
            container.add(obj);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        finally {
            putLock.unlock();
        }
        notifyNotEmpty();

    }

    public T poll(){
        T t =null;
        try {
            getLock.lockInterruptibly();

            if(container.isEmpty()){
                waitWhenEmpty();
            }

            t= container.remove(container.size()-1);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        finally {
            getLock.unlock();
        }
        notifyNotFull();
        return t;
    }

    private void waitWhenFull(){
        try{
            putLock.lock();
            putCondition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            putLock.unlock();
        }
    }

    private void notifyNotFull(){
        try{
        putLock.lock();
        putCondition.signal();
        }
        finally {
            putLock.unlock();
        }

    }

    private void waitWhenEmpty(){
        try{
            getLock.lock();
            getCondition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            getLock.unlock();
        }
    }

    private void notifyNotEmpty(){
        try{
            getLock.lock();
            getCondition.signal();
        }
        finally {
            getLock.unlock();
        }
    }

}
